`timescale 1ns / 1ps
module rhdl_pico_io(
	 input BUS_CLK_I,
    input RHDL_CLK_I,
    input RESET_I,
    input READ_I,
    input WRITE_I,
    input [31:0] ADDR_I,
    input [15:0] DATA_I,

	 input WRITE_FIFO_I,
	 input READ_FIFO_I,
	 input [15:0] FIFO_DATA_I,
	 
    output [15:0] DATA_O,
	 output [17:0] FIFO_DATA_O,
	 output [1:0] FIFO_STATUS_O,
	 output [12:0] TEST_BUS_O
);


`define IO_ADDR 16'h0B01
`define IO_STATUS_REG_ADDR 32'h0B000006

wire select;
wire select_status_reg;

wire [17:0] data_in_fifo;

wire external_fifo_full;
wire external_fifo_empty;
wire internal_fifo_full;
wire internal_fifo_empty;
wire [15:0] internal_fifo_data;

wire [15:0] io_data;
wire [15:0] status_data;

wire [12:0] test_bus;
wire [12:0] no_test_bus;

reg [1:0] status_reg;

reg [12:0] count;

reg internal_read_select_last;
reg internal_read_inc;

reg external_write_select_last;
reg external_write_inc;

reg [17:0] write_data;

wire [17:0] debug;

assign FIFO_DATA_O = debug;//{2'b11,debug[15:0]};
//6 = 64 ushort length, 16 word length (64 bit)
fifo1 #(18,4) from_external_fifo (
    .rdata(debug), 
    .wfull(external_fifo_full), 
    .rempty(external_fifo_empty), 
    .wdata(write_data), 
    .winc(external_write_inc), 
    .wclk(BUS_CLK_I), 
    .wrst_n(~RESET_I), 
    .rinc(READ_FIFO_I), 
    .rclk(RHDL_CLK_I), 
    .rrst_n(~RESET_I),
	 .TEST_BUS_O(test_bus)
    );

//6 = 128 ushort length, 42 word length (48 bit)
fifo1 #(16,4) from_internal_fifo (
    .rdata(internal_fifo_data), 
    .wfull(internal_fifo_full), 
    .rempty(internal_fifo_empty), 
    .wdata(FIFO_DATA_I), 
    .winc(WRITE_FIFO_I), 
    .wclk(RHDL_CLK_I), 
    .wrst_n(~RESET_I), 
    .rinc(internal_read_inc), 
    .rclk(BUS_CLK_I), 
    .rrst_n(~RESET_I),
	 .TEST_BUS_O(no_test_bus)
  );

//    .rinc(READ_I & select), 

always @(negedge BUS_CLK_I)
begin
	if (RESET_I)
	begin
		count <= 0;
		internal_read_inc <= 0;
		internal_read_select_last <= 0;
		external_write_inc <= 0;
		external_write_select_last <= 0;
	end
	else
	begin
		if (internal_read_select_last)
		begin
			if (~(READ_I && select))
			begin
				internal_read_inc <= 1;
			end
			else
			begin
				internal_read_inc <= 0;
			end
		end
		else
		begin
			internal_read_inc <= 0;
		end
		
		if (!external_write_select_last)
		begin
			if (WRITE_I && select)
			begin
				count <= count + 1;
				external_write_inc <= 1;
			end
			else
			begin
				count <= count;
				external_write_inc <= 0;
			end
		end
		else
		begin
			count <= count;
			external_write_inc <= 0;
		end
		
		internal_read_select_last <= (READ_I && select);
		external_write_select_last <= (WRITE_I && select);
	end
	
   write_data <= {ADDR_I[2:1],DATA_I};
	status_reg <= {external_fifo_full,internal_fifo_empty};
end

/*always @(posedge BUS_CLK_I)
begin
	status_reg <= {1'b1,internal_fifo_empty};
end
*/

assign select = (`IO_ADDR == ADDR_I[31:16]); 
assign select_status_reg = (`IO_STATUS_REG_ADDR == ADDR_I);
assign io_data = (READ_I & select & ~internal_fifo_empty) ? internal_fifo_data : 16'b0;
assign status_data = (READ_I && select_status_reg) ? {1'b1,13'b0,status_reg} : 16'b0;
//
assign DATA_O = io_data | status_data;

assign FIFO_STATUS_O = {internal_fifo_full,external_fifo_empty};

assign TEST_BUS_O = test_bus; //{external_fifo_full,external_fifo_empty,internal_fifo_full,internal_fifo_empty,4'b0110,internal_fifo_data[3:0]};

endmodule
